#pragma once

typedef struct _INTERFACE_ENTRY
{
    IID iid;
    DWORD Offset;
}INTERFACE_ENTRY, * PINTERFACE_ENTRY;

#define GET_CLASS_OFFSET(ClassName, BaseName) \
    (DWORD(static_cast<BaseName*>(reinterpret_cast<ClassName*>(0x10000000))) - 0x10000000)


#define BEGIN_INTERFACE_TABLE() \
static const INTERFACE_ENTRY *GetInterfaceTable() \
{\
    static const INTERFACE_ENTRY table[] = {\
        { IID_IUnknown, 0 }, 

#define INTERFACE_TABLE_ENTRY(ClassName, BaseName) \
        { __uuidof(BaseName), GET_CLASS_OFFSET(ClassName, BaseName) },

#define END_INTERFACE_TABLE() \
        { GUID_NULL, 0 }\
    };\
    return table;\
}

STDAPI InterfaceTableQueryInterface(
    void* pThis, 
    const INTERFACE_ENTRY* pTable, 
    REFIID riid, 
    void** ppv
)
{
    HRESULT hResult = S_OK;

    if (!ppv)
        return S_FALSE;

    *ppv = nullptr;

    if (InlineIsEqualGUID(riid, IID_IUnknown))
    {
        *ppv = pThis;
    }
    else
    {
        for (unsigned int i = 0; pTable[i].iid != GUID_NULL; i++)
        {
            if (InlineIsEqualGUID(riid, pTable[i].iid))
            {
                *ppv = (char*)pThis + pTable[i].Offset;
                break;
            }
        }

        if (*ppv == nullptr)
            hResult = S_FALSE;
    }

    return hResult;
}

#define DECLARE_DEFAULT_INTERFACE(ClassName)   \
    unsigned int m_nCnt##ClassName = 0;\
    virtual ULONG STDMETHODCALLTYPE AddRef()\
    {\
        InterlockedIncrement(&m_nCnt##ClassName);\
        return m_nCnt##ClassName;\
    }\
    virtual ULONG STDMETHODCALLTYPE Release()\
    {\
        if (m_nCnt##ClassName > 0)\
            InterlockedDecrement(&m_nCnt##ClassName);\
        if (m_nCnt##ClassName == 0)\
            delete this;\
        return m_nCnt##ClassName;\
    }\
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)\
    {\
        HRESULT hResult = InterfaceTableQueryInterface(this, GetInterfaceTable(), riid, ppvObject);\
        if (hResult == S_OK)\
            AddRef();\
        return hResult;\
    }

